package cn.com.spring.aspecti;


import cn.com.spring.BeanFactory;
import cn.com.spring.enums.AspectEnum;
import cn.com.spring.model.EntityInvocation;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * cglib代理
 *
 * @author LiuGuodong
 * @date 2023/01/28
 */
public class AspectProxy implements MethodInterceptor {

    /**
     * 未代理类
     */
    private Object unagentClass;

    public AspectProxy(Object target) {
        this.unagentClass = target;
    }

    //切面类的作用 就是将 对应方法上有注解 就去执行这个注解的对应切面逻辑
    //所以我需要一个 切面类 工厂来存放切面逻辑 并且在执行的时候去找的对应的逻辑
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Annotation[] annotations = method.getAnnotations();
        Object result = null;
        int count = 0;

        for (Annotation annotation : annotations) {
            Object aspect = BeanFactory.getAspect(annotation.annotationType());
            if (aspect == null) {
                result = methodProxy.invokeSuper(target, args);
                return result;
            }
            Class<?> aClass = aspect.getClass();
            Method[] methods = aClass.getMethods();
            AspectEnum[] values = AspectEnum.values();
            Method[] invokeMethods = new Method[values.length];

            // 执行切面方法
            for (Method m : methods) {
                for (AspectEnum value : values) {
                    if (m.getAnnotation(value.getValue()) != null) {
                        invokeMethods[value.getOrder()] = m;
                        count++;
                    }
                }
            }

            if (count == 0) {
                result = methodProxy.invokeSuper(target, args);
                return result;
            }

            for (Method invokeMethod : invokeMethods) {
                if (invokeMethod != null) {
                    // 校验是否为切面方法
                    Class<?>[] parameterTypes = invokeMethod.getParameterTypes();
                    if (parameterTypes.length == 1 && EntityInvocation.class.equals(parameterTypes[0])) {
                        result = invokeMethod.invoke(aspect, new EntityInvocation(unagentClass, method, args));
                    } else {
                        invokeMethod.invoke(aspect);
                    }
                }
            }
        }

        return result;
    }


}
